package com.poi.exp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.poi.exception.PoiElErrorCode;
import com.poi.exp.analysis.RowAnalysisStrategy;
import com.poi.exp.context.PoiExporterContext;
import com.poi.exp.domain.LoopRowModelVO;
import com.poi.exp.domain.RowModelVO;
import com.poi.exp.domain.SheetModelVO;
import com.poi.exp.domain.SingleRowModelVO;
import com.poi.exp.domain.WorkbookModelVO;
import com.poi.exp.writer.DefaultRowWriter;
import com.poi.exp.writer.LoopRowWriter;
import com.poi.log.Log;
import com.poi.util.ExcelOneSheetUtil;
import com.poi.util.PoiContextUtil;

/**
 * excel导出类
 * 
 * @author wzy
 * @date 2017年7月5日 上午9:41:52
 */
public class PoiExporter {
	private static final Logger logger = LoggerFactory.getLogger(PoiExporter.class);

	/**
	 * 向StandardEvaluationContext中注册内部函数
	 */
//	static {
//		FunctionRegister.registerInternalFunction();
//	}

	public static void export(XSSFWorkbook wb, Map<String, Object> rootObjectMap) {
		Long start = System.currentTimeMillis();

		PoiExporterContext peContext = new PoiExporterContext(new SpelExpressionParser(), rootObjectMap);

		// 分sheet进行处理
		for (int i = 0; i < wb.getNumberOfSheets(); i++) {
			logger.debug("数据长度getNumberOfSheets()={},i={}", wb.getNumberOfSheets(), i);
			XSSFSheet sheet = wb.getSheetAt(i);
			// 开始行结束行
			int j = sheet.getFirstRowNum();
			// 每行
			while (j <= sheet.getLastRowNum()) {
				logger.debug("数据行getLastRowNum()={},j={}", sheet.getLastRowNum(), j);
				XSSFRow row = sheet.getRow(j);
				if (row == null) {
					continue;
				}
				RowModelVO dealRows = RowAnalysisStrategy.getRowAnalysis(row).dealRow(row);
				logger.debug("已经处理行数dealRows={}", dealRows.getLineNum());
				j += dealRows.getLineNum();
			}
		}

		long end = System.currentTimeMillis();

		logger.info(Log.op("PoiEl#parse").msg("PoiEl解析模板耗时[{0}]ms", (end - start)).toString());
	}

	/**
	 * 导出到指定地方 des
	 * 
	 * @param templateFile
	 * @param rootObjectMap
	 * @param des
	 * @return
	 */
	public static XSSFWorkbook export2Destination(File templateFile, Map<String, Object> rootObjectMap, OutputStream des) {
		InputStream in = null;
		try {
			in = new FileInputStream(templateFile);
		} catch (FileNotFoundException e) {
			throw PoiElErrorCode.TEMPLATE_FILE_NOT_FOUND.exp(e, templateFile.getName());
		}
		return export2Destination(in, rootObjectMap, des);
	}

	/**
	 * 导出到指定地方 des
	 * 
	 * @param templateInputStream 模板
	 * @param rootObjectMap       数据
	 * @param des                 导出的位置
	 * @return
	 */
	public static XSSFWorkbook export2Destination(InputStream templateInputStream, Map<String, Object> rootObjectMap, OutputStream des) {
		XSSFWorkbook wb = null;
		try {
			wb = new XSSFWorkbook(templateInputStream);
		} catch (IOException e) {
			throw PoiElErrorCode.SYSTEM_ERROR.exp(e);
		}
		// 每次解析创建新的上线文
		PoiExporterContext poiContext = new PoiExporterContext(new SpelExpressionParser(), rootObjectMap);
		PoiContextUtil.setContext(poiContext);
		// PoiExporter.export(wb, rootObjectMap);
		// 解析excel
		WorkbookModelVO bookModelVO = analysisWorkBook(wb);
		// 写入excel
		writerWorkBook(bookModelVO);
		// 关闭资源
		try {
			wb.write(des);
			des.flush();
			des.close();
		} catch (IOException e) {
			throw PoiElErrorCode.SYSTEM_ERROR.exp(e);
		}

		return wb;
	}

	// 解析 excel
	public static WorkbookModelVO analysisWorkBook(Workbook book) {
		WorkbookModelVO bookVO = new WorkbookModelVO();
		bookVO.setBook(book);

		List<SheetModelVO> sheetList = new ArrayList<>();
		for (int i = 0, l = book.getNumberOfSheets(); i < l; i++) {
			Sheet sheet = book.getSheetAt(i);
			//
			sheetList.add(analysisSheet(sheet));
		}
		bookVO.setSheetList(sheetList);
		return bookVO;
	}

	// 解析 excel
	public static SheetModelVO analysisSheet(Sheet sheet) {
		SheetModelVO sheetVO = new SheetModelVO();
		sheetVO.setSheet(sheet);
		// 存储sheet信息，
		PoiContextUtil.getContext().getSheetModelMap().put(sheet.getSheetName(), sheetVO);
		// 记录所有合并单元格信息

		// List<CellRangeAddressModelVO> originalRangeAddressList = new
		// ArrayList<>(sheet.getNumMergedRegions());

		// List<CellRangeAddressModelVO> needRangeAddressList = new
		// ArrayList<>(sheet.getNumMergedRegions() * 3);
		// 原有的合并单元格信息
		Map<String, CellRangeAddress> originalAddreaaMap = new HashMap<>(sheet.getNumMergedRegions());
		// 新增需要合并单元格信息(长度默认三倍)
		Map<String, CellRangeAddress> needAddreaaMap = new HashMap<>(sheet.getNumMergedRegions() * 3);
		String key = null;
		for (int j = 0; j < sheet.getNumMergedRegions(); j++) {
			CellRangeAddress oldRegion = sheet.getMergedRegion(j);
			if (null != oldRegion) {
				key = ExcelOneSheetUtil.createAddressKey(oldRegion);
				originalAddreaaMap.put(key, oldRegion);
			}
		}
		sheetVO.setNeedAddreaaMap(needAddreaaMap);
		sheetVO.setOriginalAddreaaMap(originalAddreaaMap);

		List<RowModelVO> rowModelList = new ArrayList<>();
		// for (int i = sheet.getFirstRowNum(), l = (sheet.getLastRowNum() -
		// sheet.getFirstRowNum()); i < l; i++) {
		// logger.debug("数据长度getNumberOfSheets()={},i={}", wb.getNumberOfSheets(), i);
		// 开始行结束行
		// 每行
		int j = sheet.getFirstRowNum();
		while (j <= sheet.getLastRowNum()) {
			logger.debug("数据行getLastRowNum()={},j={}", sheet.getLastRowNum(), j);
			Row row = sheet.getRow(j);
			if (row == null) {
				continue;
			}
			RowModelVO rowModelVO = RowAnalysisStrategy.getRowAnalysis(row).dealRow(row);
			logger.debug("已经处理行数dealRows={}", rowModelVO.getLineNum());
			j += rowModelVO.getLineNum();
			PoiContextUtil.getContext().setAnalysisRowNum(j);
			rowModelList.add(rowModelVO);
		}

		// }

		sheetVO.setRowModelList(rowModelList);
		return sheetVO;
	}

	// 写入excel
	public static void writerWorkBook(WorkbookModelVO bookModel) {
		List<SheetModelVO> sheetModelList = bookModel.getSheetList();
		for (SheetModelVO sheetModel : sheetModelList) {
			// 从文档的最后一行之后写入数据
			PoiContextUtil.getContext().setWriterRowNum(sheetModel.getSheet().getLastRowNum() + 1);
			writerSheet(sheetModel);
		}
	}

	// 写入sheet
	public static void writerSheet(SheetModelVO sheetModel) {
		List<RowModelVO> rowModelList = sheetModel.getRowModelList();
		for (RowModelVO rowModel : rowModelList) {
			if (rowModel instanceof SingleRowModelVO) {
				DefaultRowWriter writer = new DefaultRowWriter();
				writer.writerRow(rowModel, sheetModel.getSheet());
			} else if (rowModel instanceof LoopRowModelVO) {
				LoopRowWriter writer = new LoopRowWriter();
				writer.writerRow(rowModel, sheetModel.getSheet());
			}
		}
		// 隐藏模板行
		hiddenRow(sheetModel);
		// 合并单元格
		mergeRegion(sheetModel);
	}

	// 隐藏模板行(java 操作excel删除，只是删除样式，删除内容，并不是真正的删除)
	public static void hiddenRow(SheetModelVO sheetModel) {
		for (int i = sheetModel.getSheet().getFirstRowNum(), l = PoiContextUtil.getContext().getAnalysisRowNum(); i < l; i++) {
			//sheetModel.getSheet().removeRow(sheetModel.getSheet().getRow(i));
			sheetModel.getSheet().getRow(i).setZeroHeight(true);
		}
	}

	// 合并单元格
	public static void mergeRegion(SheetModelVO sheetModel) {
		Map<String, CellRangeAddress> needMap = PoiContextUtil.getContext().getSheetModelMap().get(sheetModel.getSheet().getSheetName()).getNeedAddreaaMap();
		Iterator<String> it = needMap.keySet().iterator();
		String key = null;
		while (it.hasNext()) {
			key = it.next();
			CellRangeAddress oldAddress = needMap.get(key);
			String[] rowColum = key.split("_");
			int startRow = Integer.parseInt(rowColum[0]);
			int endRow = startRow+(oldAddress.getLastRow()-oldAddress.getFirstRow());
			CellRangeAddress newRegion = new CellRangeAddress(startRow, endRow, oldAddress.getFirstColumn(), oldAddress.getLastColumn());
			// System.out.println(desRow+","+desRow+","+oldFirstCol+","+oldLastCol);
			sheetModel.getSheet().addMergedRegion(newRegion);
		}
	}
}
